package com.college.common.utils.security;


import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.Charset;
import java.util.Arrays;

/**
 * 参数加密
 *
 * Created by an on 15/12/16.
 */
public class ParamAESCodecV2 {

    private static Logger logger = LoggerFactory.getLogger(ParamAESCodecV2.class);

    public static final Charset CHARSET = Charset.forName("utf-8");

    public static final String ALGORITHM = "AES";

    public static final String AES_CBC_NOPADDING = "AES/CBC/NoPadding";

    public static final byte keyStrSize = 16;
    public static final byte ivStrSize = 16;
    /**
     * 为了平台的通用，选择 AES/CBC/NoPadding 的模式，然后手动 padding
     * 对应PHP 平台为 mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, $iv)
     *
     * AES/CBC/NoPadding encrypt
     * 16 bytes secretKeyStr
     * 16 bytes intVector
     *
     * @param secretKeyBytes
     * @param intVectorBytes
     * @param input
     * @return
     */
    public static byte[] encryptCBCNoPadding(byte[] secretKeyBytes, byte[] intVectorBytes, byte[] input) {
        try {
            IvParameterSpec iv = new IvParameterSpec(intVectorBytes);
            SecretKey secretKey = new SecretKeySpec(secretKeyBytes, ALGORITHM);
            int inputLength = input.length;
            int srcLength;

            Cipher cipher = Cipher.getInstance(AES_CBC_NOPADDING);
            int blockSize = cipher.getBlockSize();
            byte[] srcBytes;
            if (0 != inputLength % blockSize) {
                srcLength = inputLength + (blockSize - inputLength % blockSize);
                srcBytes = new byte[srcLength];
                System.arraycopy(input, 0, srcBytes, 0, inputLength);
            } else {
                srcBytes = input;
            }

            cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);
            byte[] encryptBytes = cipher.doFinal(srcBytes);
            return encryptBytes;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * AES/CBC/NoPadding decrypt
     * 16 bytes secretKeyStr
     * 16 bytes intVector
     *
     * @param input
     * @return
     */
    public static byte[] decryptCBCNoPadding(byte[] secretKeyBytes, byte[] intVectorBytes, byte[] input) {
        try {
            IvParameterSpec iv = new IvParameterSpec(intVectorBytes);
            SecretKey secretKey = new SecretKeySpec(secretKeyBytes, ALGORITHM);

            Cipher cipher = Cipher.getInstance(AES_CBC_NOPADDING);
            cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
            byte[] encryptBytes = cipher.doFinal(input);
            return encryptBytes;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 用 AES 算法加密 inputStr。
     * 使用 secretStr 作为 key，secretStr 的前 16 个字节作为 iv。
     *
     * @param secretStr
     * @param inputStr
     * @return
     */
    public static byte[] encode(String secretStr, String inputStr) {
        byte[] secretKeyBytes = secretStr.getBytes(CHARSET);
        byte[] ivBytes = Arrays.copyOfRange(secretKeyBytes, 0, 16);
        byte[] inputBytes = inputStr.getBytes(CHARSET);

        byte[] outputBytes = encryptCBCNoPadding(secretKeyBytes, ivBytes, inputBytes);
        return outputBytes;
    }

    /**
     * 用 AES 算法加密 inputStr。
     * 使用 secretStr 作为 key，secretStr 的前 16 个字节作为 iv。
     * 并对加密后的字节数组调用 sun.misc.BASE64Encoder.encode 方法，
     * 转换成 base64 字符串返回。
     *
     * @param secretStr
     * @param inputStr
     * @return
     */
    public static String strEncodBase64(String secretStr, String inputStr){
        String base64Str = Base64.encodeBase64String(encode(secretStr, inputStr));
        logger.debug("strEncodBase64 > base64 encrypt = {}", base64Str);
        return base64Str;
    }


    /**
     * 用 AES 算法解密 inputStr。
     * 使用 secretStr 作为 key，ivStr作为 iv。
     * @param secretStr
     * @param ivStr
     * @param inputBytes
     * @return
     */
    public static byte[] decode(String secretStr, String ivStr, byte[] inputBytes){
        if (keyStrSize != secretStr.length() || ivStrSize != ivStr.length()) {
            return null;
        }
        byte[] secretKeyBytes = secretStr.getBytes(CHARSET);
        byte[] ivBytes = ivStr.getBytes(CHARSET);

        byte[] outputBytes = decryptCBCNoPadding(secretKeyBytes, ivBytes, inputBytes);
        return outputBytes;
    }


    public static String strEncodBase64(String secretStr,String ivStr, String inputStr){
        String base64Str = Base64.encodeBase64String(encode(secretStr, ivStr, inputStr));
        return base64Str;
    }




    /**
     *  用 AES 算法加密 inputStr。
     *  使用 secretStr 作为 key，ivStr作为 iv。
     *  并对加密后的字节数组调用 sun.misc.BASE64Encoder.encode 方法，
     *  转换成 base64 字符串返回。
     *  （仅作为测试用途，具体加密流程以接口文档为准）
     * @param secretStr
     * @param ivStr
     * @param inputStr
     * @return
     */
    public static byte[] encode(String secretStr, String ivStr, String inputStr) {
        if (keyStrSize != secretStr.length() || ivStrSize != ivStr.length()){
            return null;
        }
        byte[] secretKeyBytes = secretStr.getBytes(CHARSET);
        byte[] ivBytes = ivStr.getBytes(CHARSET);
        byte[] inputBytes = inputStr.getBytes(CHARSET);

        byte[] outputBytes = encryptCBCNoPadding(secretKeyBytes, ivBytes, inputBytes);
        return outputBytes;
    }
    /**
     * 用 AES 算法加密 inputStr。
     * 使用 secretStr 作为 key，secretStr 的前 16 个字节作为 iv。
     *
     * @param secretStr
     * @return
     */
    public static byte[] decode(String secretStr, byte[] inputBytes){
        byte[] secretKeyBytes = secretStr.getBytes(CHARSET);
        byte[] ivBytes = Arrays.copyOfRange(secretKeyBytes, 0, 16);

        byte[] outputBytes = decryptCBCNoPadding(secretKeyBytes, ivBytes, inputBytes);
        return outputBytes;
    }

    public static String base64StrDecode(String secretStr, String ivStr, String inputStr){
        byte[] inputBytes;
        inputBytes = Base64.decodeBase64(inputStr);
        String outputStr = new String(decode(secretStr, ivStr, inputBytes), CHARSET);
        return outputStr.trim();
    }

    /**
     * 用 AES 算法加密 inputStr。
     * 使用 secretStr 作为 key，secretStr 的前 16 个字节作为 iv。
     * 并对加密后的字节数组调用 sun.misc.BASE64Encoder.encode 方法，
     * 转换成 base64 字符串返回。
     *
     * （仅作为测试用途，具体加密流程以接口文档为准）
     *
     * @param secretStr
     * @param inputStr
     * @return
     */
    public static String base64StrDecode(String secretStr, String inputStr){
        byte[] inputBytes;
        inputBytes = Base64.decodeBase64(inputStr);
        String outputStr = new String(decode(secretStr, inputBytes), CHARSET).trim();
        logger.debug("base64Decode > base64 decrypt = {}", outputStr);
        return outputStr;
    }


    // 加密
    public static String encrypt(String sSrc, String sKey) throws Exception {
        if (sKey == null) {
            return null;
        }
        // 判断Key是否为16位
        if (sKey.length() != 16) {
            return null;
        }
        byte[] raw = sKey.getBytes("utf-8");
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");//"算法/模式/补码方式"
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
        byte[] encrypted = cipher.doFinal(sSrc.getBytes("utf-8"));

        return new Base64().encodeToString(encrypted);//此处使用BASE64做转码功能，同时能起到2次加密的作用。
    }

    // 解密
    public static String decrypt(String sSrc, String sKey) throws Exception {
        try {
            // 判断Key是否正确
            if (sKey == null) {
                return null;
            }
            // 判断Key是否为16位
            if (sKey.length() != 16) {
                return null;
            }
            byte[] raw = sKey.getBytes("utf-8");
            SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, skeySpec);
            byte[] encrypted1 = new Base64().decode(sSrc);//先用base64解密
            try {
                byte[] original = cipher.doFinal(encrypted1);
                String originalString = new String(original,"utf-8");
                return originalString;
            } catch (Exception e) {
                System.out.println(e.toString());
                return null;
            }
        } catch (Exception ex) {
            System.out.println(ex.toString());
            return null;
        }
    }




	public static final int AES_EKY_SIZE = 16;

	public static String encryptData(String content, String Key) {
		try {
			byte[] secretByte = new String(Key.getBytes(),"utf-8").getBytes();
			if (AES_EKY_SIZE == secretByte.length) {
				final SecretKeySpec secret = new SecretKeySpec(secretByte, "AES");
				final Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
				byte[] source = content.getBytes("UTF-8");
				cipher.init(Cipher.ENCRYPT_MODE, secret);
				byte[] result = cipher.doFinal(source);
				return Base64.encodeBase64String(result);
			} else {
				throw new Exception("Key size not 128");
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	public static String decryptData(String content, String Key) {
		try {
			byte[] secretByte = new String(Key.getBytes(),"utf-8").getBytes();
			if (AES_EKY_SIZE == secretByte.length) {
				final SecretKeySpec secret = new SecretKeySpec(secretByte, "AES");
				final byte[] source = Base64.decodeBase64(content);
				Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
				cipher.init(Cipher.DECRYPT_MODE, secret);
				byte[] result = cipher.doFinal(source);
				return new String(result, "UTF-8");
			} else {
				throw new Exception("Key size not 128");
			}
		} catch (Exception e) {
			//e.printStackTrace();
		}
		return null;
	}

    // public static void main(String[] args) {
    //     String secretKey = "sdf23vsc1dacsfrg";
    //     String userPhone = "17890906547";
    //     String channelNo = "test";
    //     Long time= System.currentTimeMillis();
    //     System.out.println("时间戳: "+time);
    //     JSONObject jsonObject = new JSONObject();
    //     jsonObject.put("userPhone",userPhone);
    //     String requestData = ParamAESCodecV2.strEncodBase64(secretKey,jsonObject.toJSONString());
    //     System.out.println("requestData: "+requestData);
    //     StringBuffer signData = new StringBuffer();
    //     signData.append("channelNo=");
    //     signData.append(channelNo);
    //     signData.append("&requestData=");
    //     signData.append(requestData);
    //     signData.append("&timestamp=");
    //     signData.append(time);
    //     String sign = EncryptUtil.md5(signData.toString());
    //     System.out.println("签名: "+sign);
    //
    //     //解密
    //     String responseData = ParamAESCodecV2.base64StrDecode(secretKey, requestData);
    //     System.out.println("解密数据: "+responseData);
    //
    // }
}
